package es.udc.pfc.davidparedes.model.taskservice;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import es.udc.pfc.davidparedes.model.mission.Mission;
import es.udc.pfc.davidparedes.model.mission.MissionDao;
import es.udc.pfc.davidparedes.model.shift.Shift;
import es.udc.pfc.davidparedes.model.shift.ShiftDao;
import es.udc.pfc.davidparedes.model.store.Store;
import es.udc.pfc.davidparedes.model.store.StoreDao;
import es.udc.pfc.davidparedes.model.task.Task;
import es.udc.pfc.davidparedes.model.task.TaskDao;
import es.udc.pfc.davidparedes.model.unavailable.Unavailable;
import es.udc.pfc.davidparedes.model.unavailable.UnavailableDao;
import es.udc.pfc.davidparedes.model.universe.Universe;
import es.udc.pfc.davidparedes.model.universe.UniverseDao;
import es.udc.pfc.davidparedes.model.universemanager.UniverseManager;
import es.udc.pfc.davidparedes.model.universemanager.UniverseManagerDao;
import es.udc.pfc.davidparedes.model.util.InstanceNotFoundException;
import es.udc.pfc.davidparedes.model.vendor.Vendor;
import es.udc.pfc.davidparedes.model.vendor.VendorDao;

@Service("taskService")
@Transactional
public class TaskServiceImpl implements TaskService {
	
	@Autowired
	private ShiftDao shiftDao;
	
	@Autowired
	private UnavailableDao unavailableDao;
	
	@Autowired
	private MissionDao missionDao;
	
	@Autowired
	private TaskDao taskDao;
	
	@Autowired
	private UniverseManagerDao universeManagerDao;
	
	@Autowired
	private VendorDao vendorDao;
	
	@Autowired
	private UniverseDao universeDao;
	
	@Autowired
	private StoreDao storeDao;

	public Shift addShift(Calendar start, Calendar end, char permanent, Long managerId)
			throws InstanceNotFoundException {
		UniverseManager manager = universeManagerDao.find(managerId);
		Shift shift = new Shift(start, end, permanent, manager);
		shiftDao.save(shift);
		return shift;
	}
	
	public List<Shift> addShiftsRange(Calendar start, Calendar end, char permanent, Long managerId)
			throws InstanceNotFoundException {
		UniverseManager manager = universeManagerDao.find(managerId);
		
		Calendar startDay = (Calendar) start.clone();
		startDay.set(Calendar.HOUR_OF_DAY, 0);
		startDay.set(Calendar.MINUTE, 0);
		startDay.set(Calendar.SECOND, 0);
		
		Calendar endDay = (Calendar) end.clone();
		endDay.set(Calendar.HOUR_OF_DAY, 0);
		endDay.set(Calendar.MINUTE, 0);
		endDay.set(Calendar.SECOND, 0);
		
		int n = daysBetween(startDay, endDay);
		end.set(Calendar.YEAR, start.get(Calendar.YEAR));
		end.set(Calendar.MONTH, start.get(Calendar.MONTH));
		end.set(Calendar.DAY_OF_MONTH, start.get(Calendar.DAY_OF_MONTH));
		
		List<Shift> shifts = new ArrayList<Shift>();
		Shift shift = new Shift();
		for (int i=0; i<n; i++) {
			shift = new Shift((Calendar)start.clone(), (Calendar)end.clone(), permanent, manager);
			shiftDao.save(shift);
			start.add(Calendar.HOUR_OF_DAY, 24);
			end.add(Calendar.HOUR_OF_DAY, 24);
		}
		
		return shifts;
	}
	
	public void updateShift(Long shiftId, Calendar start, Calendar end, char permanent, Long managerId)
			throws InstanceNotFoundException {
		Shift shift = shiftDao.find(shiftId);
		UniverseManager manager = universeManagerDao.find(managerId);
		shift.setStart(start);
		shift.setEnd(end);
		shift.setPermanent(permanent);
		shift.setManager(manager);
		if (start.before(end)) {
			shift.setDuration(end.getTimeInMillis()-start.getTimeInMillis());
		} else {
			shift.setDuration(0);
		}
	}
	
	public void removeShift(Long shiftId) throws InstanceNotFoundException {
		shiftDao.find(shiftId);
		shiftDao.remove(shiftId);
	}
	
	@Transactional(readOnly = true)
	public Shift findShift(long shiftId)
			throws InstanceNotFoundException {
		return shiftDao.find(shiftId);
	}
	
	public List<Shift> getShiftsByDate(Long managerId, Calendar start, Calendar end)
			throws InstanceNotFoundException {
		UniverseManager manager = universeManagerDao.find(managerId);
		List<Shift> shifts = shiftDao.findShiftsByDate(manager.getUserProfileId(), start, end);
		return shifts;
	}

	public Unavailable addUnavailable(Calendar start, Calendar end, Long vendorId)
			throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		Unavailable unavailable = new Unavailable(start, end, vendor);
		unavailableDao.save(unavailable);
		return unavailable;
	}
	
	public List<Unavailable> addUnavailablesRange(Calendar start, Calendar end, Long vendorId)
			throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		
		Calendar startDay = (Calendar) start.clone();
		startDay.set(Calendar.HOUR_OF_DAY, 0);
		startDay.set(Calendar.MINUTE, 0);
		startDay.set(Calendar.SECOND, 0);
		
		Calendar endDay = (Calendar) end.clone();
		endDay.set(Calendar.HOUR_OF_DAY, 0);
		endDay.set(Calendar.MINUTE, 0);
		endDay.set(Calendar.SECOND, 0);
		
		int n = daysBetween(startDay, endDay);
		end.set(Calendar.YEAR, start.get(Calendar.YEAR));
		end.set(Calendar.MONTH, start.get(Calendar.MONTH));
		end.set(Calendar.DAY_OF_MONTH, start.get(Calendar.DAY_OF_MONTH));
		
		List<Unavailable> unavailables = new ArrayList<Unavailable>();
		Unavailable unavailable = new Unavailable();
		for (int i=0; i<n; i++) {
			unavailable = new Unavailable((Calendar)start.clone(), (Calendar)end.clone(), vendor);
			unavailableDao.save(unavailable);
			start.add(Calendar.HOUR_OF_DAY, 24);
			end.add(Calendar.HOUR_OF_DAY, 24);
		}
		
		return unavailables;
	}
	
	public void updateUnavailable(Long unavailableId, Calendar start, Calendar end,
			Long vendorId) throws InstanceNotFoundException {
		Unavailable unavailable = unavailableDao.find(unavailableId);
		Vendor vendor = vendorDao.find(vendorId);
		unavailable.setStart(start);
		unavailable.setEnd(end);
		unavailable.setVendor(vendor);
		if (start.before(end)) {
			unavailable.setDuration(end.getTimeInMillis()-start.getTimeInMillis());
		} else {
			unavailable.setDuration(0);
		}
	}
	
	public void removeUnavailable(Long unavailableId) throws InstanceNotFoundException {
		unavailableDao.find(unavailableId);
		unavailableDao.remove(unavailableId);
	}
	
	@Transactional(readOnly = true)
	public Unavailable findUnavailable(long unavailableId)
			throws InstanceNotFoundException {
		return unavailableDao.find(unavailableId);
	}
	
	public List<Unavailable> getUnavailablesByDate(Long vendorId, Calendar start, Calendar end)
			throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		List<Unavailable> unavailables = unavailableDao.findUnavailablesByDate(vendor.getUserProfileId(), start, end);
		return unavailables;
	}

	public Mission addMission(String name, Calendar start, Calendar end, char type, Long vendorId, Long storeId)
			throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		Store store = storeDao.find(storeId);
		Mission mission = new Mission(name, start, end, type, vendor, store);
		missionDao.save(mission);
		return mission;
	}
	
	public List<Mission> addMissionsRange(String name, Calendar start, Calendar end, char type, Long vendorId, Long storeId)
			throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		Store store = storeDao.find(storeId);
		
		Calendar startDay = (Calendar) start.clone();
		startDay.set(Calendar.HOUR_OF_DAY, 0);
		startDay.set(Calendar.MINUTE, 0);
		startDay.set(Calendar.SECOND, 0);
		
		Calendar endDay = (Calendar) end.clone();
		endDay.set(Calendar.HOUR_OF_DAY, 0);
		endDay.set(Calendar.MINUTE, 0);
		endDay.set(Calendar.SECOND, 0);
		
		int n = daysBetween(startDay, endDay);
		end.set(Calendar.YEAR, start.get(Calendar.YEAR));
		end.set(Calendar.MONTH, start.get(Calendar.MONTH));
		end.set(Calendar.DAY_OF_MONTH, start.get(Calendar.DAY_OF_MONTH));
		
		List<Mission> missions = new ArrayList<Mission>();
		Mission mission = new Mission();
		for (int i=0; i<n; i++) {
			mission = new Mission(name, (Calendar)start.clone(), (Calendar)end.clone(), type, vendor, store);
			missionDao.save(mission);
			start.add(Calendar.HOUR_OF_DAY, 24);
			end.add(Calendar.HOUR_OF_DAY, 24);
		}
		
		return missions;
	}
	
	public void updateMission(Long missionId, String name, Calendar start, Calendar end, char type,
			Long vendorId, Long storeId)
			throws InstanceNotFoundException {
		Mission mission = missionDao.find(missionId);
		Vendor vendor = vendorDao.find(vendorId);
		Store store = storeDao.find(storeId);
		mission.setStart(start);
		mission.setEnd(end);
		mission.setType(type);
		mission.setVendor(vendor);
		mission.setStore(store);
		if (!(name.isEmpty())) {
			mission.setName(name);
		}
		if (start.before(end)) {
			mission.setDuration(end.getTimeInMillis()-start.getTimeInMillis());
		} else {
			mission.setDuration(0);
		}
	}
	
	public void removeMission(Long missionId) throws InstanceNotFoundException {
		missionDao.find(missionId);
		missionDao.remove(missionId);
	}
	
	@Transactional(readOnly = true)
	public Mission findMission(long missionId)
			throws InstanceNotFoundException {
		return missionDao.find(missionId);
	}
	
	public List<Mission> getMissionsByDate(Long vendorId, Calendar start, Calendar end)
			throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		List<Mission> tasks = missionDao.findMissionsByDate(vendor.getUserProfileId(), start, end);
		return tasks;
	}

	public Task addTask(Calendar start, Calendar end, char type,
			Long vendorId, Long universeId) throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		Universe universe = universeDao.find(universeId);
		Task task = new Task(start, end, type, vendor, universe);
		taskDao.save(task);
		return task;
	}
	
	public List<Task> addTasksRange(Calendar start, Calendar end, char type,
			Long vendorId, Long universeId) throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		Universe universe = universeDao.find(universeId);
		
		Calendar startDay = (Calendar) start.clone();
		startDay.set(Calendar.HOUR_OF_DAY, 0);
		startDay.set(Calendar.MINUTE, 0);
		startDay.set(Calendar.SECOND, 0);
		
		Calendar endDay = (Calendar) end.clone();
		endDay.set(Calendar.HOUR_OF_DAY, 0);
		endDay.set(Calendar.MINUTE, 0);
		endDay.set(Calendar.SECOND, 0);
		
		int n = daysBetween(startDay, endDay);
		end.set(Calendar.YEAR, start.get(Calendar.YEAR));
		end.set(Calendar.MONTH, start.get(Calendar.MONTH));
		end.set(Calendar.DAY_OF_MONTH, start.get(Calendar.DAY_OF_MONTH));
		
		List<Task> tasks = new ArrayList<Task>();
		Task task = new Task();
		for (int i=0; i<n; i++) {
			task = new Task((Calendar)start.clone(), (Calendar)end.clone(), type, vendor, universe);
			taskDao.save(task);
			start.add(Calendar.HOUR_OF_DAY, 24);
			end.add(Calendar.HOUR_OF_DAY, 24);
		}
		
		return tasks;
	}
	
	public void updateTask(Long taskId, Calendar start, Calendar end, char type,
			Long vendorId, Long universeId)
			throws InstanceNotFoundException {
		Task task = taskDao.find(taskId);
		Vendor vendor = vendorDao.find(vendorId);
		Universe universe = universeDao.find(universeId);
		task.setStart(start);
		task.setEnd(end);
		task.setType(type);
		task.setVendor(vendor);
		task.setUniverse(universe);
		if (start.before(end)) {
			task.setDuration(end.getTimeInMillis()-start.getTimeInMillis());
		} else {
			task.setDuration(0);
		}
	}
	
	public void removeTask(Long taskId) throws InstanceNotFoundException {
		taskDao.find(taskId);
		taskDao.remove(taskId);
	}
	
	@Transactional(readOnly = true)
	public Task findTask(long taskId)
			throws InstanceNotFoundException {
		return taskDao.find(taskId);
	}
	
	public List<Task> getTasksByDate(Long vendorId, Calendar start, Calendar end)
			throws InstanceNotFoundException {
		Vendor vendor = vendorDao.find(vendorId);
		List<Task> tasks = taskDao.findTasksByDate(vendor.getUserProfileId(), start, end);
		return tasks;
	}
	
	private int daysBetween(Calendar d1, Calendar d2){
		 return (int)( (d2.getTimeInMillis() - d1.getTimeInMillis()) / (1000 * 60 * 60 * 24)) + 1;
	 }
	
}
